use crate::entities::{article, article_translation, article_resource, resource};
use crate::errors::AppError;
use sea_orm::*;
use uuid::Uuid;

pub struct ArticleService;

impl ArticleService {
    pub async fn find_published_articles(
        db: &DatabaseConnection,
        language_code: Option<&str>,
        category_id: Option<Uuid>,
        limit: Option<u64>,
        offset: Option<u64>,
    ) -> Result<Vec<(article::Model, Option<article_translation::Model>)>, AppError> {
        let mut query = article::Entity::find()
            .filter(article::Column::Status.eq("published"))
            .find_with_related(article_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(article_translation::Column::LanguageCode.eq(lang));
        }
        
        if let Some(cat_id) = category_id {
            query = query.filter(article::Column::CategoryId.eq(cat_id));
        }
        
        if let Some(limit_val) = limit {
            query = query.limit(limit_val);
        }
        
        if let Some(offset_val) = offset {
            query = query.offset(offset_val);
        }
        
        let articles = query.all(db).await?;
        // Convert Vec<(Model, Vec<Model>)> to Vec<(Model, Option<Model>)>
        let result = articles.into_iter().map(|(article, translations)| {
            (article, translations.into_iter().next())
        }).collect();
        Ok(result)
    }

    pub async fn find_article_by_slug(
        db: &DatabaseConnection,
        slug: &str,
        language_code: Option<&str>,
    ) -> Result<Option<(article::Model, Option<article_translation::Model>)>, AppError> {
        let mut query = article::Entity::find()
            .filter(article::Column::Slug.eq(slug))
            .filter(article::Column::Status.eq("published"))
            .find_with_related(article_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(article_translation::Column::LanguageCode.eq(lang));
        }
        
        let articles = query.all(db).await?;
        if let Some((article, translations)) = articles.into_iter().next() {
            Ok(Some((article, translations.into_iter().next())))
        } else {
            Ok(None)
        }
    }

    pub async fn find_all_articles(
        db: &DatabaseConnection,
        language_code: Option<&str>,
        status: Option<&str>,
        category_id: Option<Uuid>,
        limit: Option<u64>,
        offset: Option<u64>,
    ) -> Result<Vec<(article::Model, Option<article_translation::Model>)>, AppError> {
        let mut query = article::Entity::find().find_with_related(article_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(article_translation::Column::LanguageCode.eq(lang));
        }
        
        if let Some(status_filter) = status {
            query = query.filter(article::Column::Status.eq(status_filter));
        }
        
        if let Some(cat_id) = category_id {
            query = query.filter(article::Column::CategoryId.eq(cat_id));
        }
        
        if let Some(limit_val) = limit {
            query = query.limit(limit_val);
        }
        
        if let Some(offset_val) = offset {
            query = query.offset(offset_val);
        }
        
        let articles = query.all(db).await?;
        // Convert Vec<(Model, Vec<Model>)> to Vec<(Model, Option<Model>)>
        let result = articles.into_iter().map(|(article, translations)| {
            (article, translations.into_iter().next())
        }).collect();
        Ok(result)
    }

    pub async fn find_article_by_id(
        db: &DatabaseConnection,
        id: Uuid,
        language_code: Option<&str>,
    ) -> Result<Option<(article::Model, Option<article_translation::Model>)>, AppError> {
        let mut query = article::Entity::find_by_id(id).find_with_related(article_translation::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(article_translation::Column::LanguageCode.eq(lang));
        }
        
        let articles = query.all(db).await?;
        if let Some((article, translations)) = articles.into_iter().next() {
            Ok(Some((article, translations.into_iter().next())))
        } else {
            Ok(None)
        }
    }

    pub async fn create_article(
        db: &DatabaseConnection,
        article_data: article::ActiveModel,
    ) -> Result<article::Model, AppError> {
        let article = article_data.insert(db).await?;
        Ok(article)
    }

    pub async fn update_article(
        db: &DatabaseConnection,
        id: Uuid,
        article_data: article::ActiveModel,
    ) -> Result<Option<article::Model>, AppError> {
        // First check if the article exists
        let existing = article::Entity::find_by_id(id).one(db).await?;
        if existing.is_none() {
            return Ok(None);
        }
        
        // Update the article
        let article = article_data.update(db).await?;
        Ok(Some(article))
    }

    pub async fn delete_article(
        db: &DatabaseConnection,
        id: Uuid,
    ) -> Result<bool, AppError> {
        let result = article::Entity::delete_by_id(id).exec(db).await?;
        Ok(result.rows_affected > 0)
    }

    pub async fn create_article_translation(
        db: &DatabaseConnection,
        translation_data: article_translation::ActiveModel,
    ) -> Result<article_translation::Model, AppError> {
        let translation = translation_data.insert(db).await?;
        Ok(translation)
    }

    pub async fn update_article_translation(
        db: &DatabaseConnection,
        id: Uuid,
        translation_data: article_translation::ActiveModel,
    ) -> Result<Option<article_translation::Model>, AppError> {
        // First check if the translation exists
        let existing = article_translation::Entity::find_by_id(id).one(db).await?;
        if existing.is_none() {
            return Ok(None);
        }
        
        // Update the translation
        let translation = translation_data.update(db).await?;
        Ok(Some(translation))
    }

    pub async fn delete_article_translation(
        db: &DatabaseConnection,
        id: Uuid,
    ) -> Result<bool, AppError> {
        let result = article_translation::Entity::delete_by_id(id).exec(db).await?;
        Ok(result.rows_affected > 0)
    }

    pub async fn get_article_resources(
        db: &DatabaseConnection,
        article_id: Uuid,
        language_code: Option<&str>,
    ) -> Result<Vec<(article_resource::Model, resource::Model)>, AppError> {
        let mut query = article_resource::Entity::find()
            .filter(article_resource::Column::ArticleId.eq(article_id))
            .find_with_related(resource::Entity);
        
        if let Some(lang) = language_code {
            query = query.filter(article_resource::Column::LanguageCode.eq(lang));
        }
        
        let results = query.all(db).await?;
        let resources = results.into_iter().flat_map(|(article_res, resources)| {
            resources.into_iter().map(move |resource| (article_res.clone(), resource))
        }).collect();
        Ok(resources)
    }

    pub async fn get_published_by_id(
        db: &DatabaseConnection,
        id: Uuid,
        language_code: Option<&str>,
    ) -> Result<crate::models::Article, AppError> {
        let article = Self::find_article_by_id(db, id, language_code).await?
            .ok_or_else(|| AppError::NotFound {
                entity: "Article".to_string(),
                id,
            })?;

        // Check if article is published
        if article.0.status != "published" {
            return Err(AppError::NotFound {
                entity: "Article".to_string(),
                id,
            });
        }

        let translations = if let Some(translation) = article.1 {
            vec![crate::models::ArticleTranslation {
                id: translation.id,
                article_id: translation.article_id,
                language_code: translation.language_code,
                title: translation.title,
                content: translation.content,
                excerpt: translation.excerpt,
                meta_title: translation.meta_title,
                meta_description: translation.meta_description,
            }]
        } else {
            vec![]
        };

        Ok(crate::models::Article {
            id: article.0.id,
            slug: article.0.slug,
            status: crate::models::PublicationStatus::from_str(&article.0.status)
                .map_err(|_| AppError::Validation("Invalid publication status".to_string()))?,
            featured_image_url: article.0.featured_image_url,
            published_at: article.0.published_at,
            created_at: article.0.created_at,
            updated_at: article.0.updated_at,
            translations,
            category_id: article.0.category_id,
        })
    }

    pub async fn list_published(
        db: &DatabaseConnection,
        options: crate::models::FilterOptions,
    ) -> Result<crate::models::PaginatedResponse<crate::models::Article>, AppError> {
        let page = options.page.unwrap_or(1);
        let per_page = options.per_page.unwrap_or(10);
        let offset = (page - 1) * per_page;

        let articles = Self::find_published_articles(
            db,
            options.language_code.as_deref(),
            options.category_id,
            Some(per_page as u64),
            Some(offset as u64),
        ).await?;

        let total = Self::count_published_articles(
            db,
            options.language_code.as_deref(),
            options.category_id,
        ).await?;

        let articles: Vec<crate::models::Article> = articles.into_iter().map(|(article, translation_opt)| {
            let translations = if let Some(translation) = translation_opt {
                vec![crate::models::ArticleTranslation {
                    id: translation.id,
                    article_id: translation.article_id,
                    language_code: translation.language_code,
                    title: translation.title,
                    content: translation.content,
                    excerpt: translation.excerpt,
                    meta_title: translation.meta_title,
                    meta_description: translation.meta_description,
                }]
            } else {
                vec![]
            };

            let status = crate::models::PublicationStatus::from_str(&article.status)
                .unwrap_or(crate::models::PublicationStatus::Draft);

            crate::models::Article {
                id: article.id,
                slug: article.slug,
                status,
                featured_image_url: article.featured_image_url,
                published_at: article.published_at,
                created_at: article.created_at,
                updated_at: article.updated_at,
                translations,
                category_id: article.category_id,
            }
        }).collect();

        Ok(crate::models::PaginatedResponse {
            data: articles,
            pagination: crate::models::Pagination {
                page,
                per_page,
                total: total as u64,
                total_pages: ((total as u32 + per_page - 1) / per_page),
            },
        })
    }

    async fn count_published_articles(
        db: &DatabaseConnection,
        language_code: Option<&str>,
        category_id: Option<Uuid>,
    ) -> Result<usize, AppError> {
        let mut query = article::Entity::find()
            .filter(article::Column::Status.eq("published"));
        
        if let Some(cat_id) = category_id {
            query = query.filter(article::Column::CategoryId.eq(cat_id));
        }
        
        let count = query.count(db).await?;
        Ok(count as usize)
    }
}